package com.itx.gatewayx.filter;

import cn.hutool.core.util.StrUtil;
import com.itx.gatewayx.common.constants.SystemConstants;
import com.itx.gatewayx.config.IgnoreUrlsConfig;
import com.itx.gatewayx.utils.JwtUtils;
import com.itx.gatewayx.utils.RedisUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;

/**
 * JWT认证过滤器
 *
 * @author itx
 */
@Slf4j
@Component
@ConditionalOnProperty(name = "spring.redis.enabled", havingValue = "true", matchIfMissing = true)
public class AuthenticationFilter implements GlobalFilter, Ordered {

    private final JwtUtils jwtUtils;
    private final RedisUtils redisUtils;
    private final List<String> ignoreUrls;
    
    public AuthenticationFilter(JwtUtils jwtUtils, RedisUtils redisUtils, IgnoreUrlsConfig ignoreUrlsConfig) {
        this.jwtUtils = jwtUtils;
        this.redisUtils = redisUtils;
        this.ignoreUrls = ignoreUrlsConfig.getIgnoreUrls();
    }
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        
        // 检查是否是不需要认证的路径
        String path = request.getURI().getPath();
        if (shouldIgnore(path)) {
            return chain.filter(exchange);
        }
        
        // 获取请求头中的token
        String token = getToken(request);
        if (StrUtil.isBlank(token)) {
            return unauthorized(response, "未提供认证令牌");
        }
        
        // 验证token有效性
        if (!jwtUtils.validateToken(token)) {
            return unauthorized(response, "无效的认证令牌");
        }
        
        // 从token中获取用户ID
        String userId = jwtUtils.extractSubject(token);
        if (StrUtil.isBlank(userId)) {
            return unauthorized(response, "认证令牌无效");
        }
        
        // 检查token是否在Redis中存在（已登录状态）
        String tokenKey = SystemConstants.LOGIN_TOKEN_KEY + userId;
        if (!redisUtils.hasKey(tokenKey)) {
            return unauthorized(response, "会话已过期，请重新登录");
        }
        
        // 检查token是否在黑名单中
        String blacklistKey = SystemConstants.BLACK_TOKEN_KEY + token;
        if (Boolean.TRUE.equals(redisUtils.hasKey(blacklistKey))) {
            return unauthorized(response, "令牌已被注销");
        }
        
        // 创建一个新的带有用户ID的请求头
        ServerHttpRequest newRequest = request.mutate()
                .header("X-User-ID", userId)
                .build();
        
        // 使用新的请求传递给下一个过滤器
        return chain.filter(exchange.mutate().request(newRequest).build());
    }
    
    /**
     * 从请求头中获取Token
     */
    private String getToken(ServerHttpRequest request) {
        String token = request.getHeaders().getFirst(SystemConstants.AUTHORIZATION_HEADER);
        if (StrUtil.isNotBlank(token) && token.startsWith(SystemConstants.TOKEN_PREFIX)) {
            return token.substring(SystemConstants.TOKEN_PREFIX.length());
        }
        return null;
    }
    
    /**
     * 判断是否是忽略认证的URL
     */
    private boolean shouldIgnore(String path) {
        return ignoreUrls.stream().anyMatch(url -> path.matches(url.replace("/**", ".*")));
    }
    
    /**
     * 返回未授权响应
     */
    private Mono<Void> unauthorized(ServerHttpResponse response, String message) {
        response.setStatusCode(HttpStatus.UNAUTHORIZED);
        return response.setComplete();
    }
    
    @Override
    public int getOrder() {
        return -1;
    }
} 